package com.fpx.metadata.tool;

import com.alibaba.druid.pool.DruidDataSource;
import com.fpx.metadata.bean.po.DbInfo;
import com.fpx.metadata.constant.SecretKeyConstant;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import lombok.extern.log4j.Log4j2;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author caowei
 */
@Log4j2
@Service
public class DynamicDataSourceManager {
    /**
     * 监听缓存过期事件，关闭数据源
     */
    private RemovalListener<DbInfo, DruidDataSource> removalListener = removal -> {
        DruidDataSource dataSource = removal.getValue();
        try {
            log.info("关闭连接池 ： {}", dataSource.getName());
            dataSource.close();
        } catch (Exception e) {
            log.error("关闭连接池异常：DruidDataSource=" + dataSource.toString(), e);
        }
    };

    /**
     * 根据数据库名缓存机器地址
     */
    private LoadingCache<DbInfo, DruidDataSource> dataSourcePoolCache = CacheBuilder.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .removalListener(removalListener)
            .build(
                    new CacheLoader<DbInfo, DruidDataSource>() {
                        public DruidDataSource load(DbInfo dbInfo) {
                            log.info("未找到 {} 对应的数据源，则从数据库重新获取...", dbInfo);
                            return createDataSourcePool(dbInfo);
                        }
                    });

    /**
     * 创建数据源连接池
     */
    public DruidDataSource createDataSourcePool(DbInfo dbInfo) {
        try {
            EnDecryptUtil enDecryptUtil = new EnDecryptUtil(SecretKeyConstant.DB_INFO_KEY + dbInfo.getHost() + dbInfo.getUser());
            String decrypt = enDecryptUtil.decrypt(dbInfo.getPassword());
            dbInfo.setPassword(decrypt);
        } catch (Exception e) {
            log.error("解密失败");
        }

        DruidDataSource druidDataSource = new DruidDataSource();
        //设置连接参数
        druidDataSource.setUrl("jdbc:mysql://" + dbInfo.getHost() + ":" + dbInfo.getPort() + "/" + dbInfo.getDatabaseName() + "?useUnicode=true&characterEncoding=utf-8");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUsername(dbInfo.getUser());
        druidDataSource.setPassword(dbInfo.getPassword());
        //配置初始化大小、最小、最大
        druidDataSource.setInitialSize(1);
        druidDataSource.setMinIdle(1);
        druidDataSource.setMaxActive(20);
        //连接泄漏监测
        druidDataSource.setRemoveAbandoned(true);
        druidDataSource.setRemoveAbandonedTimeout(30);
        //配置获取连接等待超时的时间
        druidDataSource.setMaxWait(20000);
        //配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
        druidDataSource.setTimeBetweenEvictionRunsMillis(20000);
        //防止过期
        druidDataSource.setValidationQuery("SELECT 'x'");
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTestOnBorrow(true);
        return druidDataSource;
    }

    /**
     * 获取数据源
     *
     * @return
     */
    public JdbcTemplate getDataSourcePoolByDatabase(DbInfo dbInfo) throws ExecutionException {
        DruidDataSource druidDataSource = dataSourcePoolCache.get(dbInfo);
        return new JdbcTemplate(druidDataSource);
    }

    /**
     * 关闭所有数据源连接池
     */
    @PreDestroy
    public void close() {
        log.info("关闭所有连接池");
        dataSourcePoolCache.invalidateAll();
    }

}